package es.upm.gib.mappingAPI2;

import es.upm.gib.owlbasicmodel2.OWLBasicModel2;
import org.jdom2.JDOMException;

import java.io.IOException;
import java.io.Serializable;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.Vector;

/**
 * This class is represented by a list of entries and the following metadata:
 * - Format version of the mapping
 * - Name of the mapping
 * - description of the mapping
 * - databaseinfo of the mapping
 * - List of authors of the mapping
 * - Date of creation of the mapping
 * - Last author who modifies the mapping
 * - Date of last modification of the mapping
 * - List of abbreviations
 * - CustomData
 */
public class MappingApi2 implements Serializable{

    //=======================================================================
    //ATTRIBUTES
    //=======================================================================

    /**
     * List of the entries of the mapping
     */
    private ArrayList<Entry> entries;

    /**
     * metadata of the mapping
     */
    private MappingMetadata metadata;



    //=======================================================================
    // CONSTRUCTORS
    //=======================================================================

    /**
     * create an empty Mapping
     */
    public MappingApi2() {
        this.entries = new ArrayList<Entry>();
        this.metadata = new MappingMetadata();
    }


    /**
     * create a MappingBrowser loading an existing mapping from a file
     *
     * @param mappingFile the system path of the file
     * @throws JDOMException if it is not able to parse the file with JDOM
     * @throws IOException JDOM can not build the file to create a jdom.Document
     * @throws ParseException The project is not able to parse a date of the XML
     */
    public MappingApi2(String mappingFile) throws JDOMException, IOException, ParseException {
        loadMapping(mappingFile);
    }


    //=======================================================================
    // PUBLIC METHODS
    //=======================================================================

    /**
     * Retrieves list of entries of the mapping
     * @return entries of the mapping
     */
    public ArrayList<Entry> getEntries() {
        return entries;
    }

    /**
     * Set entries for the mapping
     * @param entries entries to store to the model
     */
    public void setEntries(ArrayList<Entry> entries) {
        this.entries = entries;
    }

    /**
     * Set the metadata for the mapping
     * @param metadata metadata to add
     */
    public void setMetadata(MappingMetadata metadata) {
        this.metadata = metadata;
    }

    /**
     * Retrieves the metadata of the mapping
     * @return metadata of the mapping
     */
    public MappingMetadata getMetadata() {
        return metadata;
    }

    /**
     * the methods load a previously saved mapping.
     * <br>
     * the file has to be an xml file that respect the DTD given above.
     *
     * @param mappingFile an xml file representing a mapping (it has to respect the defined format)
     * @throws JDOMException if it is not able to parse the file with JDOM
     * @throws IOException JDOM can not build the file to create a jdom.Document
     * @throws ParseException The project is not able to parse a date of the XML
     */
    public void loadMapping(String mappingFile) throws JDOMException, ParseException, IOException {


        FormatEncoder formatEncoder = new FormatEncoder();

        MappingApi2 mappingApi2 = formatEncoder.readFromXML(mappingFile);

        this.entries = mappingApi2.getEntries();

        this.metadata = mappingApi2.getMetadata();

    }


    /**
     * the method adds an entry to the mapping.<br>
     * the entry added is the one provided as parameter
     *
     * @param entry the Entry object that is added to the mapping
     */

    public void addEntry(Entry entry) {

        if (entry != null) {
            entries.add(entry);
        }
    }

    /**
     * deletes the entry in the given position of the mapping.
     *
     * @param position the position in the mapping that will be delete
     */

    public void deleteEntry(int position) {
        if (entries != null && entries.size() > position) {
            //mapping.removeElementAt(position);
            entries.remove(position);
        }
    }

    /**
     * finds the Entry that contains the searched conceptual view
     *
     * @param conceptualSearch the conceptual View (its Integer identifier)
     * @return the Entry that contains the searched conceptual view
     */
    public Entry conceptualToPhysical(Integer conceptualSearch) {
        if (entries == null || conceptualSearch < 0 || conceptualSearch >= entries.size()) {
            return null;
        }
        //entry.normalize();
        return entries.get(conceptualSearch);
    }

    /**
     * finds the Entry that contains the searched physical view
     *
     * @param physicalSearch the physical View
     * @return the Entry that contains the searched physical view
     */
    public Entry physicalToConceptual(View physicalSearch) {
        if (entries == null) {
            return null;
        }
        for (Entry entry : entries) {
            if (entry != null && entry.getPhysicalView() != null && entry.getPhysicalView().equals(physicalSearch)) {
                return entry;
            }
        }
        return null;
    }

    /**
     * <pre>
     * Given a View, the method extract all the subsumed Views existing in the conceptual part of the mapping.
     * Given a View, another View is subsumed if:
     * <p/>
     *    1. The set of paths of the second view is subsumed to the set of paths of the first view.
     * <p/>
     *    2. the restriction of the second view are subsumed to the restriction of the first view
     * <p/>
     * <p/>
     * </pre>
     *
     * @param searchView the subsumed path in Path format
     * @param owlModel   the RDF/OWL model used to check if the path is subsumed
     * @return a Vector containing the list of the Views contained in the mapping that subsumes the View passed as parameter
     *         (their corresponding entry Integer identifiers)
     */
    public Vector<Integer> findConceptualSubsumedViews(View searchView, OWLBasicModel2 owlModel) {
        Vector<Integer> res = new Vector<Integer>();
        if (searchView == null) {
            return null;
        }
        if (entries == null) {
            return null;
        }


        for (int i = 0; i < entries.size(); i++) {
            // System.out.println("enter findConceptualSubsumedPaths");
            if (entries.get(i) != null) {
                View check = entries.get(i).getConceptualView();
                if (check != null && check.isSubsumed(searchView, owlModel)) {
                    //System.out.println("FOUND A PATH "+check);
                    res.add(i);
                }
            }
        }
        // System.out.println(res);
        return res;
    }

    /**
     * <pre>
     * Given a View, the method extract all the subsumed Views existing in the physical part of the mapping.
     * Given a View, another View is subsumed if:
     * <p/>
     *    1. The set of paths of the second view is subsumed to the set of paths of the first view.
     * <p/>
     *    2. the restriction of the second view are subsumed to the restriction of the first view
     * <p/>
     * <p/>
     * </pre>
     *
     * @param searchView the subsumed path in Path format
     * @param owlModel   the RDF/OWL model used to check if the path is subsumed
     * @return a Vector containing the list of the Views contained in the mapping that subsumes the View passed as parameter
     */
    public Vector<View> findPhysicalSubsumedPaths(View searchView, OWLBasicModel2 owlModel) {
        Vector<View> res = new Vector<View>();
        if (entries == null) {
            return null;
        }
        for (Entry entry : entries) {

            if (entry != null) {
                View check = entry.getPhysicalView();
                if (check != null && check.isSubsumed(searchView, owlModel)) {
                    res.add(check);
                }
            }
        }
        return res;
    }

}
